﻿using AlgoritmoConstrutivo.entidades;
using Entidades;
using Entidades.entidades;
using RandomKeyGenerator;
using Support;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Algoritmos.clustersPorVizinho
{
    public class ClustersPorVizinhosDinamico : Algoritmo
    {
        //private NosHandler estratégiaAlocacaoNos;
        
        private NosHandler estrategia_1_AlocacaoNos, estrategia_2_AlocacaoNos;
        
     

        public override Grafo Decoder(int numCromossos, Grafo software, Cromossomo cromossomo, int individuo, Configuration2 cfg)
        {
      
            software.IdOriginalCromossomo = individuo;
            base.DefineQtdeClusters(cromossomo, software);

            Array.Sort(cromossomo.Chaves); // odenação de n posições - O(n log n)

            int i = 0;

            //Para cada cluster avaliado, avaliar cada um de seus módulos. O(nm)
            for (int cluster = 0; cluster < NUM_CLUSTERS; cluster++)
            {
                i = 0;
                No n = null;

                do
                {
                    Gene moduloSemCluster = cromossomo.Chaves[i];

                    n = software.RetornaNo(moduloSemCluster.Indice);
                    i++;

                } while (n.Cluster != null && i != software.RetornarNumNos());

                if (i == software.RetornarNumNos()) //já varreu a lista toda de nós.
                    break;

                Cluster cl = software.RetornarCluster(cluster);

                software.ConfigurarClusterDeUmNo(cl, n);

                this.AtribuirVizinhos(n, cl, software,cfg);
            }

            int melhorCluster = -1;
           

            foreach (Gene gene in cromossomo.Chaves)
            {
                No n = software.RetornaNo(gene.Indice);

                if (n.Cluster != null)
                    continue;

                this.ConfigurarEstrategiaAlocacao("Algoritmos.clustersPorVizinho.NosHandler_BestFit", "Algoritmos.clustersPorVizinho.NosHandler_LeastBestFit");

                if (gene.KeyCriterio <= 0.5)
                    this.estrategia_1_AlocacaoNos.alocarNos(n, NUM_CLUSTERS, software, out melhorCluster);          
                else
                    this.estrategia_2_AlocacaoNos.alocarNos(n, NUM_CLUSTERS, software, out melhorCluster);          


                if (melhorCluster != -1)
                    software.ConfigurarClusterDeUmNo(software.RetornarCluster(melhorCluster), n);
                else //este else é necessário para que no passo do pos processamento, estes modulos sem cluster possam fazer parte do processo. Sem eles, eles nao estavam sendo
                // considerados como modulos de clusters unitários. 
                {
                    Cluster clUnitarios = new Cluster(true);
                    software.ConfigurarClusterDeUmNo(clUnitarios, n);
                }
            }

            base.ColocaModulosUnitariosMenosPiorPorCromossomo(cromossomo.Chaves, software);

           cromossomo.Chaves = cromossomo.Chaves.OrderBy(x => x.Indice).ToArray();
            
           
            //é necessário ordená-los de volta a partir de seus índices, para que o crossover possa ocorrer de forma correta. No crossover, 
            //comparamos a chave aleatório do módulo i do pai elite com a chave aleatoria do modulo i do pai nao elite. Se eu nao desordena-los agora, 
            //a chave aleatoria i do pai elite representa um modulo diferente da chave aleatoria i do pai nao elite

            return software;
        }

        public void ConfigurarEstrategiaAlocacao(string estrategia1, string estrategia2)
        {

            if (this.estrategia_1_AlocacaoNos == null && this.estrategia_2_AlocacaoNos == null)
        {
            Assembly assembly = Assembly.Load("Algoritmos");
                Type t = assembly.GetType(estrategia1);
                Type t2 = assembly.GetType(estrategia2);


                this.estrategia_1_AlocacaoNos = (NosHandler)Activator.CreateInstance(t);
                this.estrategia_2_AlocacaoNos = (NosHandler)Activator.CreateInstance(t2);
            }
        }

        private void AtribuirVizinhos(No n, Cluster cl, Grafo software, Configuration2 cfg)
        {

           // double numVizinhos = Math.Round((double) n.ArestasQueSaem.Count * Entidades.Configuration.Porc_Vizinhos);
            double numVizinhos = Math.Round((double)n.ArestasQueSaem.Count * cfg.Porc_Vizinhos);

            int vizinhos = (int) numVizinhos;


            for (int i = 0; i < vizinhos; i++)
            {
                if (n.ArestasQueSaem[i].Cluster == null)
                    software.ConfigurarClusterDeUmNo(cl, n.ArestasQueSaem[i]);
            }
        }

        public override Grafo Decoder(int numCromossos, Grafo software, Cromossomo[] chaves, int individuo)
        {
            throw new NotImplementedException();
        }



        public override void ConfigurarEstratégiaAlocacao(string estrategia)
        {
            
        }

        public override Grafo Decoder(int numCromossos, Grafo software, Cromossomo cromossomo, int individuo, string estrategiaAlocacao)
        {
            throw new NotImplementedException();
        }

        public override Grafo Decoder(int numCromossos, Grafo software, Cromossomo cromossomo, int individuo, String estrategiaAlocacao,Configuration2 cfg)
        {
            throw new NotImplementedException();
        }

        
    }
}
